#include<iostream>
#include <pthread.h>
#include<iomanip>
#include <iostream>
#include<vector>
#include<semaphore.h>
#include <arm_neon.h>
#include<sys/time.h>
using namespace std;
int taskNum = 1;
int pThreadNum = 1;
int n;
float** m;
void generate()
{
    for (int i = 0; i < n; i++)
    {
        m[i][i] = 1.0;
        for (int p = i + 1; p < n; p++)
        {
            m[i][p] = rand() % 100 + 1;
        }
    }
    for (int k = 0; k < n; k++)
    {
        for (int i = k + 1; i < n; i++)
        {
            for (int p = 0; p < n; p++)
            {
                m[i][p] += m[k][p];
            }
        }
    }
}
void show()
{
    for (int i = 0; i < n; i++)
    {
        for (int p = 0; p < n; p++)
        {
            cout << setw(5) << m[i][p] << " ";
        }
        cout << endl;
    }
}
void commonGaosi()
{
    for (int i = 0; i < n; i++)
    {
        for (int j = i + 1; j < n; j++)
        {
            m[i][j] /= m[i][i];
        }
        for (int k = i + 1; k < n; k++)
        {
            for (int j = i + 1; j < n; j++)
            {
                m[k][j] -= m[i][j] * m[k][i];
            }
            m[k][i] = 0;
        }
    }
}
void init()
{
    m = new float* [n];
    for (int p = 0; p < n; p++)
    {
        m[p] = new float[n];
        for (int q = 0; q < n; q++)
        {
            m[p][q] = 0.0;
        }
    }
    generate();
}



typedef struct
{
    int t_id; 
}threadParam_t;


sem_t sem_main;
sem_t* sem_workerstart;
sem_t* sem_workerend;


void* ssethreadFunc(void* param)
{
    threadParam_t* p = (threadParam_t*)param;
    int t_id = p->t_id; 
    int begin = 0;

    int j = 0;
    float32x4_t t1, t2, vx, vaik;
    for (int k = 0; k < n; k++)
    {
        begin = k + t_id * taskNum + 1;
        sem_wait(&sem_workerstart[t_id]);


        for (int i = begin; (i < begin + taskNum) && (i < n); i++)
        {
            vaik = vdupq_n_f32(m[i][k]);
            for (j = k + 1; j + 4 < n; j += 4)
            {
                t1 = vld1q_f32(&m[i][j]);
                t2 = vld1q_f32(&m[k][j]);
                vx = vmulq_f32(vaik, t2);
                t1 = vsubq_f32(t1, vx);
                vst1q_f32(&m[i][j], t1);
            }
            for (; j < n; j++)
            {
                m[i][j] = m[i][j] - m[i][k] * m[k][j];
            }
            m[i][k] = 0;
        }
        sem_post(&sem_main);
        sem_wait(&sem_workerend[t_id]);
    }


    pthread_exit(NULL);

}
void* threadFunc(void* param)
{
    threadParam_t* p = (threadParam_t*)param;
    int t_id = p->t_id; 
    int begin = 0;


    for (int k = 0; k < n; k++)
    {
        begin = k + t_id * taskNum + 1;
        sem_wait(&sem_workerstart[t_id]);


        for (int i = begin; (i < begin + taskNum) && (i < n); i++)
        {
            for (int j = k + 1; j < n; j++)
            {
                m[i][j] = m[i][j] - m[i][k] * m[k][j];
            }
            m[i][k] = 0;
        }
        sem_post(&sem_main);
        sem_wait(&sem_workerend[t_id]);
    }


    pthread_exit(NULL);
}





void gaosi(int type)
{
    sem_init(&sem_main, 0, 0);
    for (int i = 0; i < pThreadNum; i++)
    {
        sem_init(&sem_workerstart[i], 0, 0);
        sem_init(&sem_workerend[i], 0, 0);
    }
    pthread_t* handle = new pthread_t[pThreadNum];
    threadParam_t* param = new threadParam_t[pThreadNum];
    for (int id = 0; id < pThreadNum; id++)
    {
        param[id].t_id = id;
        switch (type)
        {
        case 0:
            pthread_create(&handle[id], NULL, threadFunc, (void*)&param[id]);
            break;
        case 1:
            pthread_create(&handle[id], NULL, ssethreadFunc, (void*)&param[id]);
            break;
        }
    }

    for (int k = 0; k < n; k++)
    {
        for (int j = k + 1; j < n; j++)
        {
            m[k][j] = m[k][j] / m[k][k];
        }
        m[k][k] = 1.0;
        for (int id = 0; id < pThreadNum; id++)
            sem_post(&sem_workerstart[id]);
        for (int id = 0; id < pThreadNum; id++)
            sem_wait(&sem_main);
        for (int id = 0; id < pThreadNum; id++)
            sem_post(&sem_workerend[id]);
    }
    for (int id = 0; id < pThreadNum; id++)
        pthread_join(handle[id], NULL);

}





int main()
{
    pThreadNum = 10;
    sem_workerstart = new sem_t[pThreadNum];
    sem_workerend = new sem_t[pThreadNum];



    struct timeval t1, t2;
    double timeuse;

    for (int i = 1; i <= 20; i++)
    {
        n = i * 200;
        taskNum = n / pThreadNum;
        init();
        gettimeofday(&t1, NULL);
        gaosi(0);
        gaosi(1);
        commonGaosi();
        gettimeofday(&t2, NULL);
        timeuse = (t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec) / 1000000.0;
        cout << timeuse << endl;
    }
    sem_destroy(&sem_main);
    for (int i = 0; i < pThreadNum; i++)
    {
        sem_destroy(&sem_workerstart[i]);
        sem_destroy(&sem_workerend[i]);
    }
    return 0;
}